package com.ist412.pocke;

import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

/**
 * This class represents the activity in which users actually perform the workouts that they've created within the application.
 * <p>
 * This functionality is dependent on Workouts having Exercises which contain Sets. If this is met, this activity will pull in
 * all the Exercises and Sets from the workout signified from WorkoutActivity or CalendarActivity. This is targeted by the
 * "workoutslot" extra put in the previous file.
 * <p>
 * Once the Workout is signified, this activity filters out all Exercises which don't have any valid Sets and ignores all Sets which
 * lack a valid amount of reps. In addition, notes are displayed for the user to see.
 * <p>
 * Once the user finishes an exercise, they are asked how many Reps they completed. This information is logged and recorded to the
 * database. A 60 second cool-down timer is then started. The user is warned at 0:10, 0:02, 0:01, and 0:00 seconds before a sound
 * is played to signal the start of the next set.
 * <p>
 * Once the next set starts, the user is shown the name of the Exercise, the reps they want to complete, and any notes they've entered.
 * 
 * @throws IllegalArgumentException	Thrown when an invalid workoutslot is supplied.
 * 
 * @author Rob C
 * @version 1.2
 */
public class ProgressActivity extends Activity implements OnClickListener {
	
	protected enum TickReturn 		{ CONTINUE, WARN, NEXT };
	protected final String			_TICK_KEY = "progressworkouttick";
	protected ArrayList<Exercise> 	_exercises;
	protected ArrayList<Set>		_sets;
	protected RestTimer				_restTimer;
	protected RestHandler			_restHandler;
	protected volatile boolean		_resting, _workoutFinished;
	protected int					_workoutslot, _currentMin, _currentSec, _currentExercise, _currentSet;
	
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		// Remove title & Make fullscreen
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);
        
		setContentView(R.layout.activity_progress);
		
		MainActivity.loadData();
		
		_exercises			= new ArrayList<Exercise>();
		_sets				= new ArrayList<Set>();
		
		// Used on txtTimer
		_currentMin			= 0;
		_currentSec			= 0;
		_currentExercise	= -1;
		_currentSet			= -1;
		
		_restHandler		= new RestHandler();
		
		_resting			= false;
		_workoutFinished 	= false;
		
		// Fill Exercises
		Intent intent = getIntent();
		Bundle b = intent.getExtras();
		
		_workoutslot	= b.getInt("workoutslot",-1); // slot in MainActivity._workouts
		
		if (_workoutslot < 0) {
			try {
				Log.d("ProgressWorkoutActivity","Did not receive proper slot for workout. Workouts size: " + MainActivity._workouts.size() + " received slot: " + _workoutslot);
				throw new IllegalArgumentException("Did not receive proper slot for workout. Workouts size: " + MainActivity._workouts.size() + " received slot: " + _workoutslot);
			} catch (IllegalArgumentException iae) { finish(); return; }
		} else {
			_exercises 	= MainActivity._workouts.get(_workoutslot).getExercises();
		}
		
		// Set up buttons
		Button 		btnNext		= (Button) findViewById(R.id.progress_btnNext);
		Button		btnSkip		= (Button) findViewById(R.id.progress_btnSkip);
		ImageView 	imgBack		= (ImageView) findViewById(R.id.progress_imgBack);
		
		btnNext.setOnClickListener(this);
		btnSkip.setOnClickListener(this);
		imgBack.setOnClickListener(this);
		
		// Pull in information for the first set of the first exercise in the first workout
		for (int i = 0; i < _exercises.size(); i++) {
			if (_exercises.get(i).getSets().size() > 0) {
				_currentExercise = i;
				_currentSet = 0;
				break;
			}
		}
		
		// Check that a legal exercise and set have been submitted to the activity
		if (_currentExercise < 0 || _currentSet < 0) {
			try {
				showToast(MainActivity._workouts.get(_workoutslot).getName()  + " doesn't have any valid Exercises or Sets");
				throw new IllegalArgumentException(MainActivity._workouts.get(_workoutslot).getName()  + " does not have any Exercises with valid Sets");
			} catch (IllegalArgumentException iae) { finish(); return; }
		} else {
			//Log.e("Progress","_currentExercise: " + _currentExercise + " | _currentset: " + _currentSet);
			setMenu(_currentExercise, _currentSet);
			
			// Handle situation where only one set is available
			if (!workoutHasMoreSets()) {
				btnSkip.setVisibility(View.GONE);
				btnNext.setText("Finish");
			}
		}
		
	} // end onCreate()
	
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
			case R.id.progress_btnNext:
				if (!_workoutFinished) {
					int reps = 0;
					EditText edtReps = (EditText) findViewById(R.id.progress_edtReps);
					
					if (edtReps.getText().length() > 0) {
						try {
							reps = Integer.parseInt(edtReps.getText().toString());
						} catch (NumberFormatException nfe) {
							reps = 0;
							showToast("Invalid reps entered");
							return;
						}
					} else {
						showToast("Invalid reps entered.");
						return;
					}
					
					// TODO Log reps here or within function
					// Check if more exercises exist
					if (workoutHasMoreSets()) {
						startRest();
					} else {
						
						Button btnSkip = (Button) findViewById(R.id.progress_btnSkip);
						btnSkip.setVisibility(View.GONE);
						
						stopWorkout();
					}
				} else {
					// Reset workout
					for (int i = 0; i < _exercises.size(); i++) {
						if (_exercises.get(i).getSets().size() > 0) {
							_currentExercise = i;
							_currentSet = 0;
							break;
						}
					}
					
					Button btnNext = (Button) findViewById(R.id.progress_btnNext);
					EditText edtReps = (EditText) findViewById(R.id.progress_edtReps);
					
					btnNext.setText("Next");
					edtReps.setText("");
					
					_workoutFinished = false;
					_resting = false;
					
					setMenu(_currentExercise, _currentSet);
				}
				break;
			case R.id.progress_btnSkip:
				// TODO Record skip or 0 reps
				if (!_workoutFinished && workoutHasMoreSets()) {
					nextSet();
				}
				break;
			case R.id.progress_imgBack:
				// RestTimer should be handled here with onPause or onStop
				finish();
				break;
			case R.id.progressrest_btnSkip:
				nextSet();
				break;
			case R.id.progressrest_imgBack:
				// RestTimer should be handled here with onPause or onStop
				finish();
				break;
		}
	} // end onClick()
	
	@Override
	public void onPause() {
		super.onPause();
		// Stop the thread if active
		if (_restTimer != null) {
			_restTimer.interrupt();
		}
	} // end onPause()
	
	@Override
	public void onStop() {
		super.onStop();
		// Stop the thread if active
		if (_restTimer != null) {
			_restTimer.interrupt();
		}
	} // end onStop()
	
	@Override
	public void onDestroy() {
		super.onDestroy();
		// Stop the thread if active
		if (_restTimer != null) {
			_restTimer.interrupt();
		}
	} // end onDestroy()
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * This function serves as the logic behind how to handle the timer's tick functionality.
	 * <p>
	 * TickReturn.NEXT 		- Start next exercise, play finishing sound
	 * TickReturn.WARN 		- Play Warning Second
	 * TickReturn.CONTINUE	- Do nothing; just reduce the second count
	 * 
	 * @return TickReturn NEXT, WARN, or CONTINUE depending on object _currentSec and _currentMin
	 */
	protected final TickReturn tick() {
		_currentSec--;
		
		// Check to advance minute
		if (_currentSec < 0) {
			_currentMin--;
			_currentSec = 59;
		}
		
		if (_currentMin < 0) {
			_currentMin = 0;
			_currentSec = 0;
			return TickReturn.NEXT;
			
		} else if (_currentMin == 0) {
			if (_currentSec <= 2 || _currentSec == 10) {
				return TickReturn.WARN;
			}
		}
		
		return TickReturn.CONTINUE;
	}
	
	/**
	 * This function will convert a time in miliseconds to mm:ss format.
	 * 
	 * @param time	Time in miliseconds
	 * @return	String in time format mm:ss of the time entered in miliseconds
	 */
	protected final String timeDisplay(int time) {
		String retstr = "";
		int min = time / 60000;
		int sec = (time % 60000) / 1000;
		retstr = Integer.toString(min) + ":";
		if (sec < 10) {
			retstr += "0" + Integer.toString(sec);
		} else {
			retstr += Integer.toString(sec);
		}
		return retstr;
	}
	
	/**
	 * This function will convert two integers representing minutes and seconds into a mm:ss format
	 * 
	 * @param min	Time in minutes
	 * @param sec	Time in seconds
	 * @return	String in the time format mm:ss of the time entered in miliseconds
	 */
	protected final String timeDisplay(int min, int sec) {
		String retstr = "";
		retstr = Integer.toString(min) + ":";
		if (sec < 10) {
			retstr += "0" + Integer.toString(sec);
		} else {
			retstr += Integer.toString(sec);
		}
		return retstr;
	}
	
	/**
	 * This function updates the TextView which display the time information
	 */
	protected final void updateClock() {
		// This function prevents inner classes from calling things directly
		if (_resting) {
			TextView txtTimer = (TextView) findViewById(R.id.progressrest_txtTime);
			txtTimer.setText(timeDisplay(_currentMin, _currentSec));
		}
	}
	
	/**
	 * This function sets the Activity's _currentMin and _currentSet variables.
	 * 
	 * @param exslot
	 * @param setslot
	 */
	protected final void setTime(int min, int sec) {
		_currentMin = min;
		_currentSec = sec;
	}
		
	/**
	 * Sets variables signaling that the workout has started and it starts the
	 * exerciseTimer thread to begin receiving tick() responses.
	 */
	protected final void startRest() {
		Log.e("Progress", "Rest from MainActivity: " + MainActivity._restTimer);
		_resting = true;
		
		// Get user set rest timer
		int min = 0, sec = 0, time = 0;
		time = MainActivity._restTimer;
		min = time / 60000;
		sec = (time % 60000) / 1000;
		
		Log.e("Progress","Computed time: " + min + ":" + sec);
		
		setTime(min, sec);
		
		// Start rest timer
		_restTimer = new RestTimer();
		_restTimer.start();
		
		// Change to rest view
		setContentView(R.layout.progress_rest);
		
		// Setup mnue
		TextView txtWorkoutName		= (TextView) findViewById(R.id.progressrest_txtWorkoutName);
		TextView txtExerciseName	= (TextView) findViewById(R.id.progressrest_txtExerciseName);
		TextView txtReps			= (TextView) findViewById(R.id.progressrest_txtRep);
		
		txtWorkoutName.setText(MainActivity._workouts.get(_workoutslot).getName());
		
		// Find next exercise name
		String nextExercise = "";
		int nextRep			= 0;
		Exercise e = _exercises.get(_currentExercise);
		
		if ((e.getSets().size() - 1) > _currentSet) {
			nextExercise = e.getName();
			nextRep = e.getSets().get(_currentSet + 1).getRep();
		} else {
			for (int i = _currentExercise; i < _exercises.size(); i ++) {
				e = _exercises.get(i);
				if (e.getSets().size() > 0) {
					nextExercise = e.getName();
					nextRep = e.getSets().get(0).getRep();
				}
			}
		}
		
		txtExerciseName.setText(nextExercise);
		txtReps.setText(Integer.toString(nextRep));
		
		// Setup buttons
		Button btnSkip = (Button) findViewById(R.id.progressrest_btnSkip);
		ImageView imgBack = (ImageView) findViewById(R.id.progressrest_imgBack);
		
		btnSkip.setOnClickListener(this);
		imgBack.setOnClickListener(this);
		
		updateClock();
	} // end startWorkout()
	
	/**
	 * Sets variables signaling that the workout was stopped
	 */
	protected final void stopWorkout() {
		_workoutFinished = true;
		try {
			_restTimer.interrupt();
		} catch (NullPointerException npe) { }
		showToast("Workout finished - Good job!");
		Button btnNext = (Button) findViewById(R.id.progress_btnNext);
		btnNext.setText("Reset");
	}
	
	/**
	 * Updates menu to display next Set in the Workout.
	 */
	protected final void nextSet() {
		// Find next set in workout
		Exercise e;
		
		if (!workoutHasMoreSets()) {
			try {
				finish();
				throw new Exception("Attempting to start new Set without sets remaining.");
			} catch (Exception exception) {
				showToast("Attempting to start new Set without sets remaining.");
				exception.printStackTrace();
				finish();
			}
		}
		
		// Check for more Sets in the current Exercise
		e = _exercises.get(_currentExercise);
		
		if (_currentSet < (e.getSets().size() - 1)) {
			_currentSet ++;
			
		} else {
			// Otherwise, check if more Exercises have Sets
			for (int i = (_currentExercise + 1); i < _exercises.size(); i++) {
				
				e = _exercises.get(i);
				
				if (e.getSets().size() > 0) {
					_currentExercise = i;
					_currentSet = 0;
					break;
				}
			} // end for i
		} // end if .. else (find sets)
		
		Button btnNext, btnSkip;
		
		// Reset menu if resting
		if (_resting) {
			_resting = false;
			_restTimer.interrupt();
		
			setContentView(R.layout.activity_progress);
			
			btnNext				= (Button) findViewById(R.id.progress_btnNext);
			btnSkip				= (Button) findViewById(R.id.progress_btnSkip);
			ImageView imgBack	= (ImageView) findViewById(R.id.progress_imgBack);
			
			btnNext.setOnClickListener(this);
			btnSkip.setOnClickListener(this);
			imgBack.setOnClickListener(this);
			
		}
		
		setMenu(_currentExercise, _currentSet);
		
		if (!workoutHasMoreSets()) {
			btnNext = (Button) findViewById(R.id.progress_btnNext);
			btnSkip = (Button) findViewById(R.id.progress_btnSkip);
			
			btnNext.setText("Finish");
			btnSkip.setVisibility(View.GONE);
		}
		
	} // end nextSet()
	
	/**
	 * Utility function to determine if more Sets exist after the current Set
	 * in the exercise.
	 * 
	 * @return boolean true if more sets exist, false if no
	 */
	protected final boolean workoutHasMoreSets() {
		Exercise e = _exercises.get(_currentExercise);
		//Log.e("Progress", "_currentExercise: " + _currentExercise + "| _currentSet: " + _currentSet + " | e.getSets().size(): " + e.getSets().size());
		if ((e.getSets().size() - 1) > _currentSet) {
			return true;
		} else {
			for (int i = (_currentExercise + 1); i < _exercises.size(); i++) {
				e = _exercises.get(i);
				if (e.getSets().size() > 0) {
					return true;
				}
			}
		}
		return false;
	} // end workoutHasMoresets()
	
	/**
	 * This function displays all information about the given Set on the screen.
	 * 
	 * @param exslot	Slot for _exercises
	 * @param setslot	Slot for _exercises.get()
	 */
	protected final void setMenu(int exslot, int setslot) {
		if (_resting) {
			return;
		}
		
		Exercise e = _exercises.get(exslot);
		Set set = e.getSets().get(setslot);
		
		TextView txtExerciseName 	= (TextView) findViewById(R.id.progress_txtExerciseName);
		TextView txtExerciseCount 	= (TextView) findViewById(R.id.progress_txtExerciseCount);
		TextView txtSetCount		= (TextView) findViewById(R.id.progress_txtSetCount);
		TextView txtRepCount		= (TextView) findViewById(R.id.progress_txtRepCount);
		TextView txtWeight			= (TextView) findViewById(R.id.progress_txtWeight);
		TextView txtNote			= (TextView) findViewById(R.id.progress_txtNote);
		
		txtExerciseName.setText(e.getName());
		txtExerciseCount.setText((exslot + 1) + " / " + _exercises.size());
		txtSetCount.setText((setslot + 1) + " / " + e.getSets().size());
		
		if (set.getExhaust()) {
			txtRepCount.setText("Exhaust");
		} else {
			txtRepCount.setText(Integer.toString(set.getRep()));
		}
		
		if (set.getWeight() > 0) {
			txtWeight.setText(Integer.toString(set.getWeight()));
			txtWeight.setVisibility(View.VISIBLE);
		} else {
			txtWeight.setText("");
			txtNote.setVisibility(View.GONE);
		}
		
		if (set.getNote().length() > 0) {
			txtNote.setText("Note: " + set.getNote());
			txtWeight.setVisibility(View.VISIBLE);
		} else {
			txtNote.setText("");
			txtNote.setVisibility(View.GONE);
		}
		
	} // end setMenu()
	
	/**
	 * This function will handle playing sounds within the activity.
	 * @param soundid	R.raw identifier for the sound
	 */
	protected final void playSound(int soundid) {
		// Play sound
		final MediaPlayer media = MediaPlayer.create(ProgressActivity.this, soundid);
		media.setOnCompletionListener(new OnCompletionListener() {
			@Override
			public void onCompletion(MediaPlayer mp) {
				media.release();
			}
		});
		media.start();
	}
	
	/**
	 * Utility function to show Toast. Set to Toast.LENGTH_SHORT
	 * @param text	Text to display in the toast
	 */
	protected final void showToast(String text) {
		Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
	}
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * This class will call the Tick() function every second until stopped.
	 * <p>
	 * The return of the tick() function is linked to the parent string _TICK_KEY.
	 * 
	 * @author Rob C
	 * @version 1.0
	 * @see Thread
	 */
	private final class RestTimer extends Thread {
		
		public RestTimer() { }
		
		@Override
		public void run() {
			try {
				// Wait a second before running
				sleep(1000);
				synchronized (this) {
					while (!_workoutFinished) {
						//Log.e("ProgressWorkoutActivity","tick() - _exslot: " + _exslot + " _setslot: " + _setslot);
						
						// Call Tick & Process Result
						TickReturn tickReturn = tick();
						
						Message msgObj = _restHandler.obtainMessage();
	                    Bundle b = new Bundle();
	                    b.putString(_TICK_KEY, tickReturn.toString());
	                    msgObj.setData(b);
	                    _restHandler.sendMessage(msgObj);
						
						// Wait 1 second
						sleep(1000);
					}
				} // end synchronized(this)
			} catch (InterruptedException ie) {
				ie.printStackTrace();
			} // end try .. catch (ie)
		} // end run()
		
	} // end inner class StartExerciseSet
	
	/**
	 * This class handles the return sent from ExerciseTimer.
	 * <p>
	 * TickReturn.NEXT 		- Searches for next available set
	 * 						  If none available: congratulates user on finish and calls stopWorkout()  
	 * TickReturn.WARN		- Plays warning sound
	 * TickReturn.CONTINUE	- No effect
	 * 
	 * @author Rob C
	 * @version 1.0
	 * @see Handler
	 */
	private final class RestHandler extends Handler {
		
		@Override
		public void handleMessage(Message envelope) {			
			Exercise e 	= _exercises.get(_currentExercise);
			Set set		= e.getSets().get(_currentSet);
			
			// Get return of tick()
			String msg = envelope.getData().getString(_TICK_KEY);
			
			// Handle tick() return			
			if (msg == "NEXT") {
				// Play finish sound
				playSound(R.raw.finish);
				// Vibrate
				if (MainActivity._vibrateTime > 0) {
					Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
					vibe.vibrate(MainActivity._vibrateTime);
				}
				nextSet();
				
			} else if (msg == "WARN") {
				// Vibrate
				if (MainActivity._vibrateTime > 0) {
					Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
					vibe.vibrate(MainActivity._vibrateTime);
				}
				// Play warning sound
				playSound(R.raw.warn);
				
			} // end if .. else ("NEXT" or "WARN")
			
			// Always update clock
			updateClock();
			
		} // end handleMessage()
		
	} // end inner class ExerciseSetHandler
	
} // end class ProgressWorkoutActivity